# libraries
library(forcats)
library(lubridate)
library(plotly)
library(readr)
library(tidyverse)
# data
scoobydoo <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-07-13/scoobydoo.csv')
scooby doo

scooby doo

Introduction

This weeks Tidy Tuesday dataset comes from Kaggle by way of manual data aggregation from plummye.

Every Scooby-Doo episode and movie’s various variables.

Took ~1 year to watch every Scooby-Doo iteration and track every variable. Many values are subjective by nature of watching but I tried my hardest to keep the data collection consistent.

If you plan to use this data for anything school/entertainment related you are free to (credit is always welcome).

Notebook can be viewed by pasting the github URL to this document into https://nbviewer.jupyter.org/.

scoobydoo %>% head(5)
# values
color_scheme1 <- c(
  "#228B22", # aka forest green (Best)
  "#98FB98", # aka pale green (Top 3)
  "#D3D3D3", # aka light grey (Other)
  "#FFB6C1", # aka light pink (Bottom 3)
  "#DC143C" # aka crimson (Worst)
  )

color_scheme2 <- c(
  "#228B22", # aka forest green (Great)
  "#98FB98", # aka pale green (OK)
  "#FFB6C1", # aka light pink (Meh)
  "#DC143C" # aka crimson (Worst)
  )

the_gang <- c("Fred", "Daphnie", "Velma", "Shaggy", "Scooby")

IMDb Time Series Analysis

By Series

## get summary info for each season
series_info <- scoobydoo %>%
  filter(
    !(season %in% c("Movie", "Special"))
  ) %>%
  mutate(
    year = year(date_aired),
    imdb = as.double(imdb),
    engagement = as.double(engagement)
  ) %>%
  group_by(series_name, network) %>%
  dplyr::summarise(
    series_start = min(date_aired),
    series_end = max(date_aired),
    n_episodes = n(),
    mean_imdb = mean(imdb, na.rm = TRUE),
    mean_engagement = mean(engagement, na.rm = TRUE)
  ) %>%
  ungroup() %>%
  filter(n_episodes > 1) %>% # filter out movie events
  arrange(series_start, series_end) %>%
  mutate(
    series_id = as.double(row_number())
  )

series_info <- series_info %>%
  mutate(
    ranking = case_when(
      series_id == head(arrange(series_info, desc(mean_imdb)), 1)$series_id ~ "Best Series",
      series_id %in% head(arrange(series_info, desc(mean_imdb)), 3)$series_id ~ "Top 3 Series",
      series_id == head(arrange(series_info, mean_imdb), 1)$series_id ~ "Worst Series",
      series_id %in% head(arrange(series_info, mean_imdb), 3)$series_id ~ "Bottom 3 Series",
      TRUE ~ "Other"
    ),
    ranking = factor(ranking, levels = c("Best Series", "Top 3 Series", "Other", "Bottom 3 Series", "Worst Series"))
  )
# plot series over time
series_info %>%
  plot_ly(
    type = 'bar',
    mode = 'markers',
    x = ~series_id,
    y = ~mean_imdb,
    color = ~ranking,
    colors = color_scheme1,
    text = ~paste0("<b>", series_name, "</b><br>",
                   "<i>Aired from ", series_start, " to ", series_end, " on ", network, "</i><br><br>",
                   "Mean IMDb Score: ", round(mean_imdb, 2), " (Number of Reviews: ", round(mean_engagement), ")<br>",
                   "Episodes: ", n_episodes, "<br>")
  ) %>%
  layout(
    title = 'IMDb Scores for Scooby Doo Series Over Time',
    xaxis = list(title = 'Sequential Series Number', showticklabels = FALSE),
    yaxis = list(title = 'Mean IMDb Score'),
    legend = list(orientation = 'h', y = -0.3),
    width = 900,
    height = 400
  )

By Seasons

## get summary info for each season
season_info <- scoobydoo %>%
  mutate(
    year = year(date_aired),
    imdb = as.double(imdb),
    engagement = as.double(engagement)
  ) %>%
  group_by(series_name, network, season) %>%
  dplyr::summarise(
    season_start = min(date_aired),
    season_end = max(date_aired),
    n_episodes = n(),
    mean_imdb = mean(imdb, na.rm = TRUE),
    mean_engagement = mean(engagement, na.rm = TRUE)
  ) %>%
  ungroup() %>%
  filter(n_episodes > 1 & !(season %in% c("Movie", "Special"))) %>% # filter out movie events
  arrange(season_start, season_end) %>%
  mutate(
    season_id = as.double(row_number())
  )

season_info <- season_info %>%
  mutate(
    ranking = case_when(
      season_id == head(arrange(season_info, desc(mean_imdb)), 1)$season_id ~ "Best Season",
      season_id %in% head(arrange(season_info, desc(mean_imdb)), 3)$season_id ~ "Top 3 Season",
      season_id == head(arrange(season_info, mean_imdb), 1)$season_id ~ "Worst Season",
      season_id %in% head(arrange(season_info, mean_imdb), 3)$season_id ~ "Bottom 3 Season",
      TRUE ~ "Other"
    ),
    ranking = factor(ranking, levels = c("Best Season", "Top 3 Season", "Other", "Bottom 3 Season", "Worst Season"))
  )

The average Scooby Doo TV series has 2.0625 seasons. That’s so few!

# plot season over time
season_info %>%
  plot_ly(
    type = 'bar',
    mode = 'markers',
    x = ~season_id,
    y = ~mean_imdb,
    color = ~ranking,
    colors = color_scheme1,
    text = ~paste0("<b>", series_name, " - Season ", season, "</b><br>",
                   "<i>Aired from ", season_start, " to ", season_end, " on ", network, "</i><br><br>",
                   "Mean IMDb Score: ", round(mean_imdb, 2), " (Number of Reviews: ", round(mean_engagement), ")<br>",
                   "Episodes: ", n_episodes, "<br>")
  ) %>%
  layout(
    title = 'IMDb Scores for Scooby Doo Seasons Over Time',
    xaxis = list(title = 'Sequential Season Number', showticklabels = FALSE),
    yaxis = list(title = 'Mean IMDb Score'),
    legend = list(orientation = 'h', y = -0.3),
    width = 900,
    height = 400
  )

By Episodes

scoobydoo %>%
  left_join(series_info, by = c("series_name", "network")) %>%
    filter(
    !(is.na(imdb)),
    imdb != "NULL",
    engagement != "NULL",
    !(season %in% c("Movie", "Special"))
  ) %>%
  plot_ly(
    type = 'scatter',
    mode = 'markers',
    x = ~index,
    y = ~imdb,
    color = ~ranking,
    colors = color_scheme1,
    text = ~paste0("<b>", title, "</b><br>",
                  "<i>Season ", season, " of Series ", series_name, "</i><br><br>",
                  "Aired ", date_aired, " on ", network, "<br>",
                  "IMDb Score ", imdb, " (Number of Reviews: ", engagement, ")")
  ) %>%
  layout(
    title = 'IMDb scores of Scooby Doo episodes over time',
    xaxis = list(title = 'Episode Index (according to Scoobypedia)'),
    yaxis = list(title = 'IMDb Score'),
    width = 900,
    height = 400,
    legend = list(orientation = 'h', y = -0.3)
  )

Monsters

# collapse columns
monsters <- scoobydoo %>%
  mutate(
    # caught
    caught_fred = str_replace(caught_fred, "TRUE", "Fred"),
    caught_daphnie = str_replace(caught_daphnie, "TRUE", "Daphnie"),
    caught_velma = str_replace(caught_velma, "TRUE", "Velma"),
    caught_shaggy = str_replace(caught_shaggy, "TRUE", "Shaggy"),
    caught_scooby = str_replace(caught_scooby, "TRUE", "Scooby"),
    
    # captured
    captured_fred = str_replace(captured_fred, "TRUE", "Fred"),
    captured_daphnie = str_replace(captured_daphnie, "TRUE", "Daphnie"),
    captured_velma = str_replace(captured_velma, "TRUE", "Velma"),
    captured_shaggy = str_replace(captured_shaggy, "TRUE", "Shaggy"),
    captured_scooby = str_replace(captured_scooby, "TRUE", "Scooby"),
    
    # unmasked
    unmask_fred = str_replace(unmask_fred, "TRUE", "Fred"),
    unmask_daphnie = str_replace(unmask_daphnie, "TRUE", "Daphnie"),
    unmask_velma = str_replace(unmask_velma, "TRUE", "Velma"),
    unmask_shaggy = str_replace(unmask_shaggy, "TRUE", "Shaggy"),
    unmask_scooby = str_replace(unmask_scooby, "TRUE", "Scooby"),
  )

Who caught the monsters?

# tidy data frame for monsters caught
caught_df <- monsters
caught_df$caught_by <- apply(caught_df %>% select(starts_with("caught_")), 1, function(x) paste(x[x != "FALSE" & x != "NULL"], collapse = ","))
caught_df$caught_by[is.na(caught_df$caught_by)] <- "Not Caught"
caught_df <- caught_df %>%
  separate_rows(caught_by, sep = ",") %>%
  filter(
    caught_by %in% the_gang
  )
# table of most catches
count(caught_df, caught_by) %>%
  arrange(desc(n))

Who was captured by monsters the most?

# tidy data frame for captured by the monster
captured_df <- monsters
captured_df$captured <- apply(captured_df %>% select(starts_with("captured_")), 1, function(x) paste(x[x != "FALSE" & x != "NULL"], collapse = ","))
#captured$captured_by[is.na(captured$caught_by)] <- "Not Caught"
captured_df <- captured_df %>%
  separate_rows(captured, sep = ",") %>%
  filter(
    captured %in% the_gang
  )
count(captured_df, captured) %>%
  arrange(desc(n))
# plot the relationship
captured_df2 <- captured_df %>%
  mutate(
    year = year(date_aired),
    real = ifelse(monster_real == "TRUE", "Real", "Fake"),
    monster_details = paste0(monster_name, " (", real, " Monster(s)) in '", title, "' on ", date_aired, ".")
  ) %>%
  group_by(year, captured) %>%
  dplyr::summarise(
    n = n(),
    captured_details = paste(monster_details, collapse = "\n")
  )

plot_monsters_captures <- function(gang_member, df = captured_df2) {
  fig <- df %>%
    filter(captured == gang_member) %>%
    plot_ly(
      type = 'bar',
      x = ~year,
      y = ~n,
      text = ~paste0("<b>Captured By...</b><br><br>",
                     captured_details),
      showlegend = FALSE
    ) %>%
    layout(
      xaxis = list(
        showline = TRUE,
        showticklabels = FALSE,
        mirror = "ticks",
        linecolor = toRGB("black"),
        linewidth = 2,
        range = c(min(captured_df2$year), max(captured_df2$year))
      ),
      yaxis = list(
        title = ~paste0("<b>", gang_member, "</b>"),
        showline = TRUE,
        mirror = "ticks",
        linecolor = toRGB("black"),
        linewidth = 2,
        range = c(min(captured_df2$n), max(captured_df2$n) + 1)
      )
    )
  
  fig
}

subplot(
  plot_monsters_captures("Fred"),
  plot_monsters_captures("Daphnie"),
  plot_monsters_captures("Velma"),
  plot_monsters_captures("Shaggy"),
  plot_monsters_captures("Scooby"),
  nrows = length(the_gang),
  shareX = FALSE,
  shareY = FALSE,
  titleX = FALSE,
  titleY = TRUE
) %>%
  layout(
    mode = 'lines+markers',
    title = '<b><i>Jinkies! ... has been captured by the monster!</i></b>',
    width = 900,
    height = 600,
    legend = list(orientation = 'h')
  )

Who unmasked the most monsters?

# tidy data frame for captured by the monster
unmasked_df <- monsters
unmasked_df$unmasked_by <- apply(unmasked_df %>% select(starts_with("unmask_")), 1, function(x) paste(x[x != "FALSE" & x != "NULL"], collapse = ","))
unmasked_df$unmasked_by[is.na(unmasked_df$unmasked_by)] <- "Not Unmasked"
unmasked_df <- unmasked_df %>%
  separate_rows(unmasked_by, sep = ",") %>%
  filter(
    unmasked_by %in% the_gang
  )
count(unmasked_df, unmasked_by) %>%
  arrange(desc(n))
# plot the relationship
unmasked_df2 <- unmasked_df %>%
  mutate(
    year = year(date_aired),
    real = ifelse(monster_real == "TRUE", "Real", "Fake"),
    monster_details = paste0(monster_name, " (", real, " Monster(s)) in '", title, "' on ", date_aired, ".")
  ) %>%
  group_by(year, unmasked_by) %>%
  dplyr::summarise(
    n = n(),
    unmask_details = paste(monster_details, collapse = "\n")
  )

plot_monsters_unmasks <- function(gang_member, df = unmasked_df2) {
  fig <- df %>%
    filter(unmasked_by == gang_member) %>%
    plot_ly(
      type = 'bar',
      x = ~year,
      y = ~n,
      text = ~paste0("<b>Unmasked By...</b><br><br>",
                     unmask_details),
      showlegend = FALSE
    ) %>%
    layout(
      xaxis = list(
        showline = TRUE,
        showticklabels = FALSE,
        mirror = "ticks",
        linecolor = toRGB("black"),
        linewidth = 2,
        range = c(min(unmasked_df2$year), max(unmasked_df2$year))
      ),
      yaxis = list(
        title = ~paste0("<b>", gang_member, "</b>"),
        showline = TRUE,
        mirror = "ticks",
        linecolor = toRGB("black"),
        linewidth = 2,
        range = c(min(unmasked_df2$n), max(unmasked_df2$n) + 1)
      )
    )
  
  fig
}

subplot(
  plot_monsters_unmasks("Fred"),
  plot_monsters_unmasks("Daphnie"),
  plot_monsters_unmasks("Velma"),
  plot_monsters_unmasks("Shaggy"),
  plot_monsters_unmasks("Scooby"),
  nrows = length(the_gang),
  shareX = FALSE,
  shareY = FALSE,
  titleX = FALSE,
  titleY = TRUE
) %>%
  layout(
    mode = 'lines+markers',
    title = '<b><i>Monsters Unmasked Annualy By...</i></b>',
    width = 900,
    height = 600,
    legend = list(orientation = 'h')
  )

What monster types are most likely to get away?

n_not_caught <- nrow(filter(scoobydoo, caught_not == "TRUE"))
got_away_rate <- paste0(round(100 * (n_not_caught / nrow(scoobydoo)), 2), "%")

First of all, the monster(s) got away at the end of only 31 out of 603 episodes (rate of 5.14%). Of those 31, the following breaks down the success rate by monster type.

escape_stats <- scoobydoo %>%
  filter(caught_not == "TRUE") %>%
  # looks like there were a few mispellings
  mutate(monster_type = ifelse(str_detect(monster_type, "(Disguised|Disguised|Disugised)"), "Disguised", monster_type)) %>%
  separate_rows(monster_type, sep = ",") %>%
  group_by(monster_type) %>%
  dplyr::summarise(
    n_escaped = n(),
    mean_imdb_escaped = mean(as.double(imdb))
  )

imdb_effect_categories <- c(
  "Better Than Average Episode",
  "Slightly Better Than Average Episode",
  "Slightly Worse Than Average Episode",
  "Worse Than Average Episode"
)

escape_stats2 <- scoobydoo %>%
  mutate(monster_type = ifelse(str_detect(monster_type, "(Disguised|Disguised|Disugised)"), "Disguised", monster_type)) %>%
  separate_rows(monster_type, sep = ",") %>%
  filter(imdb != "NULL") %>%
  group_by(monster_type) %>%
  dplyr::summarise(
    n_total = n(),
    mean_imdb = mean(as.double(imdb))
  ) %>%
  inner_join(escape_stats) %>%
  mutate(
    escape_rate = round(100 * (n_escaped / n_total)),
    escape_imdb_effect = mean_imdb_escaped - mean_imdb,
    imdb_effect_category = case_when(
      escape_imdb_effect < -1 ~ "Worse Than Average Episode",
      escape_imdb_effect < 0 ~ "Slightly Worse Than Average Episode",
      escape_imdb_effect < 1 ~ "Slightly Better Than Average Episode",
      escape_imdb_effect >= 1 ~ "Better Than Average Episode"
    ),
    imdb_effect_category = factor(imdb_effect_category, levels = imdb_effect_categories)
  )

escape_stats2 %>%
  plot_ly(
    type = 'bar',
    x = ~fct_reorder(monster_type, escape_rate, .desc = TRUE),
    y = ~escape_rate,
    color = ~imdb_effect_category,
    colors = color_scheme2,
    text = ~paste0("<b>", monster_type, "</b><br><br>", 
                   "Escaped ", n_escaped, " out of ", n_total, " episodes.<br>",
                   "Average Episode IMDb Score (All): ", round(mean_imdb, 1), "<br>",
                   "Average Episode IMDb Score (Escaped): ", round(mean_imdb_escaped, 1))
  ) %>%
  layout(
    title = 'How often do Scooby Doo monsters go uncaught, and how does that affect the episode?',
    xaxis = list(title = "Monster Type"),
    yaxis = list(title = "Escape Rate (%)"),
    legend = list(orientation = 'h', y = -0.3),
    width = 900,
    height = 500
  )
LS0tDQp0aXRsZTogIjIwMjEwNzEzIC0gU2Nvb2J5IERvbyINCmF1dGhvcjogIk5pY2sgQ3J1aWNrc2hhbmsiDQpkYXRlOiAiNy8xNC8yMDIxIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICBkZl9wcmludDogcGFnZWQNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSkNCmBgYA0KDQpgYGB7ciBsaWJyYXJpZXN9DQojIGxpYnJhcmllcw0KbGlicmFyeShmb3JjYXRzKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQpgYGB7cn0NCiMgZGF0YQ0Kc2Nvb2J5ZG9vIDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIxLzIwMjEtMDctMTMvc2Nvb2J5ZG9vLmNzdicpDQpgYGANCg0KIVtzY29vYnkgZG9vXShodHRwczovL21lZGlhLmF2YWxvbmhpbGwud2l6YXJkcy5jb20vc3R5bGVzL3NlY29uZF9odWJwYWdlX2Jhbm5lci9wdWJsaWMvaW1hZ2VzL2R5bmFtaWNodWJwYWdlL2x3Y2xrd2N3cWNuLmpwZykNCg0KIyBJbnRyb2R1Y3Rpb24NCg0KVGhpcyB3ZWVrcyBbVGlkeSBUdWVzZGF5XShodHRwczovL2dpdGh1Yi5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L3RyZWUvbWFzdGVyL2RhdGEvMjAyMS8yMDIxLTA3LTEzKSBkYXRhc2V0IGNvbWVzIGZyb20gW0thZ2dsZV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS93aWxsaWFtc2Nob29sZW1hbi9zY29vYnlkb28tY29tcGxldGUpIGJ5IHdheSBvZiBtYW51YWwgZGF0YSBhZ2dyZWdhdGlvbiBmcm9tIFtwbHVtbXllXShodHRwczovL3d3dy5rYWdnbGUuY29tL3dpbGxpYW1zY2hvb2xlbWFuKS4NCg0KPiBFdmVyeSBTY29vYnktRG9vIGVwaXNvZGUgYW5kIG1vdmllJ3MgdmFyaW91cyB2YXJpYWJsZXMuDQo+DQo+IFRvb2sgfjEgeWVhciB0byB3YXRjaCBldmVyeSBTY29vYnktRG9vIGl0ZXJhdGlvbiBhbmQgdHJhY2sgZXZlcnkgdmFyaWFibGUuIE1hbnkgdmFsdWVzIGFyZSBzdWJqZWN0aXZlIGJ5IG5hdHVyZSBvZiB3YXRjaGluZyBidXQgSSB0cmllZCBteSBoYXJkZXN0IHRvIGtlZXAgdGhlIGRhdGEgY29sbGVjdGlvbiBjb25zaXN0ZW50Lg0KPg0KPiBJZiB5b3UgcGxhbiB0byB1c2UgdGhpcyBkYXRhIGZvciBhbnl0aGluZyBzY2hvb2wvZW50ZXJ0YWlubWVudCByZWxhdGVkIHlvdSBhcmUgZnJlZSB0byAoY3JlZGl0IGlzIGFsd2F5cyB3ZWxjb21lKS4NCg0KTm90ZWJvb2sgY2FuIGJlIHZpZXdlZCBieSBwYXN0aW5nIHRoZSBnaXRodWIgVVJMIHRvIHRoaXMgZG9jdW1lbnQgaW50byBodHRwczovL25idmlld2VyLmp1cHl0ZXIub3JnLy4NCg0KYGBge3J9DQpzY29vYnlkb28gJT4lIGhlYWQoNSkNCmBgYA0KDQpgYGB7cn0NCiMgdmFsdWVzDQpjb2xvcl9zY2hlbWUxIDwtIGMoDQogICIjMjI4QjIyIiwgIyBha2EgZm9yZXN0IGdyZWVuIChCZXN0KQ0KICAiIzk4RkI5OCIsICMgYWthIHBhbGUgZ3JlZW4gKFRvcCAzKQ0KICAiI0QzRDNEMyIsICMgYWthIGxpZ2h0IGdyZXkgKE90aGVyKQ0KICAiI0ZGQjZDMSIsICMgYWthIGxpZ2h0IHBpbmsgKEJvdHRvbSAzKQ0KICAiI0RDMTQzQyIgIyBha2EgY3JpbXNvbiAoV29yc3QpDQogICkNCg0KY29sb3Jfc2NoZW1lMiA8LSBjKA0KICAiIzIyOEIyMiIsICMgYWthIGZvcmVzdCBncmVlbiAoR3JlYXQpDQogICIjOThGQjk4IiwgIyBha2EgcGFsZSBncmVlbiAoT0spDQogICIjRkZCNkMxIiwgIyBha2EgbGlnaHQgcGluayAoTWVoKQ0KICAiI0RDMTQzQyIgIyBha2EgY3JpbXNvbiAoV29yc3QpDQogICkNCg0KdGhlX2dhbmcgPC0gYygiRnJlZCIsICJEYXBobmllIiwgIlZlbG1hIiwgIlNoYWdneSIsICJTY29vYnkiKQ0KYGBgDQoNCiMgSU1EYiBUaW1lIFNlcmllcyBBbmFseXNpcw0KDQojIyBCeSBTZXJpZXMNCg0KYGBge3J9DQojIyBnZXQgc3VtbWFyeSBpbmZvIGZvciBlYWNoIHNlYXNvbg0Kc2VyaWVzX2luZm8gPC0gc2Nvb2J5ZG9vICU+JQ0KICBmaWx0ZXIoDQogICAgIShzZWFzb24gJWluJSBjKCJNb3ZpZSIsICJTcGVjaWFsIikpDQogICkgJT4lDQogIG11dGF0ZSgNCiAgICB5ZWFyID0geWVhcihkYXRlX2FpcmVkKSwNCiAgICBpbWRiID0gYXMuZG91YmxlKGltZGIpLA0KICAgIGVuZ2FnZW1lbnQgPSBhcy5kb3VibGUoZW5nYWdlbWVudCkNCiAgKSAlPiUNCiAgZ3JvdXBfYnkoc2VyaWVzX25hbWUsIG5ldHdvcmspICU+JQ0KICBkcGx5cjo6c3VtbWFyaXNlKA0KICAgIHNlcmllc19zdGFydCA9IG1pbihkYXRlX2FpcmVkKSwNCiAgICBzZXJpZXNfZW5kID0gbWF4KGRhdGVfYWlyZWQpLA0KICAgIG5fZXBpc29kZXMgPSBuKCksDQogICAgbWVhbl9pbWRiID0gbWVhbihpbWRiLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fZW5nYWdlbWVudCA9IG1lYW4oZW5nYWdlbWVudCwgbmEucm0gPSBUUlVFKQ0KICApICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGZpbHRlcihuX2VwaXNvZGVzID4gMSkgJT4lICMgZmlsdGVyIG91dCBtb3ZpZSBldmVudHMNCiAgYXJyYW5nZShzZXJpZXNfc3RhcnQsIHNlcmllc19lbmQpICU+JQ0KICBtdXRhdGUoDQogICAgc2VyaWVzX2lkID0gYXMuZG91YmxlKHJvd19udW1iZXIoKSkNCiAgKQ0KDQpzZXJpZXNfaW5mbyA8LSBzZXJpZXNfaW5mbyAlPiUNCiAgbXV0YXRlKA0KICAgIHJhbmtpbmcgPSBjYXNlX3doZW4oDQogICAgICBzZXJpZXNfaWQgPT0gaGVhZChhcnJhbmdlKHNlcmllc19pbmZvLCBkZXNjKG1lYW5faW1kYikpLCAxKSRzZXJpZXNfaWQgfiAiQmVzdCBTZXJpZXMiLA0KICAgICAgc2VyaWVzX2lkICVpbiUgaGVhZChhcnJhbmdlKHNlcmllc19pbmZvLCBkZXNjKG1lYW5faW1kYikpLCAzKSRzZXJpZXNfaWQgfiAiVG9wIDMgU2VyaWVzIiwNCiAgICAgIHNlcmllc19pZCA9PSBoZWFkKGFycmFuZ2Uoc2VyaWVzX2luZm8sIG1lYW5faW1kYiksIDEpJHNlcmllc19pZCB+ICJXb3JzdCBTZXJpZXMiLA0KICAgICAgc2VyaWVzX2lkICVpbiUgaGVhZChhcnJhbmdlKHNlcmllc19pbmZvLCBtZWFuX2ltZGIpLCAzKSRzZXJpZXNfaWQgfiAiQm90dG9tIDMgU2VyaWVzIiwNCiAgICAgIFRSVUUgfiAiT3RoZXIiDQogICAgKSwNCiAgICByYW5raW5nID0gZmFjdG9yKHJhbmtpbmcsIGxldmVscyA9IGMoIkJlc3QgU2VyaWVzIiwgIlRvcCAzIFNlcmllcyIsICJPdGhlciIsICJCb3R0b20gMyBTZXJpZXMiLCAiV29yc3QgU2VyaWVzIikpDQogICkNCmBgYA0KDQpgYGB7ciBzY29vYnkgc2VyaWVzIGltZGIgdGltZWxpbmV9DQojIHBsb3Qgc2VyaWVzIG92ZXIgdGltZQ0Kc2VyaWVzX2luZm8gJT4lDQogIHBsb3RfbHkoDQogICAgdHlwZSA9ICdiYXInLA0KICAgIG1vZGUgPSAnbWFya2VycycsDQogICAgeCA9IH5zZXJpZXNfaWQsDQogICAgeSA9IH5tZWFuX2ltZGIsDQogICAgY29sb3IgPSB+cmFua2luZywNCiAgICBjb2xvcnMgPSBjb2xvcl9zY2hlbWUxLA0KICAgIHRleHQgPSB+cGFzdGUwKCI8Yj4iLCBzZXJpZXNfbmFtZSwgIjwvYj48YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiPGk+QWlyZWQgZnJvbSAiLCBzZXJpZXNfc3RhcnQsICIgdG8gIiwgc2VyaWVzX2VuZCwgIiBvbiAiLCBuZXR3b3JrLCAiPC9pPjxicj48YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiTWVhbiBJTURiIFNjb3JlOiAiLCByb3VuZChtZWFuX2ltZGIsIDIpLCAiIChOdW1iZXIgb2YgUmV2aWV3czogIiwgcm91bmQobWVhbl9lbmdhZ2VtZW50KSwgIik8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiRXBpc29kZXM6ICIsIG5fZXBpc29kZXMsICI8YnI+IikNCiAgKSAlPiUNCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gJ0lNRGIgU2NvcmVzIGZvciBTY29vYnkgRG9vIFNlcmllcyBPdmVyIFRpbWUnLA0KICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdTZXF1ZW50aWFsIFNlcmllcyBOdW1iZXInLCBzaG93dGlja2xhYmVscyA9IEZBTFNFKSwNCiAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAnTWVhbiBJTURiIFNjb3JlJyksDQogICAgbGVnZW5kID0gbGlzdChvcmllbnRhdGlvbiA9ICdoJywgeSA9IC0wLjMpLA0KICAgIHdpZHRoID0gOTAwLA0KICAgIGhlaWdodCA9IDQwMA0KICApDQpgYGANCg0KIyMgQnkgU2Vhc29ucw0KDQpgYGB7cn0NCiMjIGdldCBzdW1tYXJ5IGluZm8gZm9yIGVhY2ggc2Vhc29uDQpzZWFzb25faW5mbyA8LSBzY29vYnlkb28gJT4lDQogIG11dGF0ZSgNCiAgICB5ZWFyID0geWVhcihkYXRlX2FpcmVkKSwNCiAgICBpbWRiID0gYXMuZG91YmxlKGltZGIpLA0KICAgIGVuZ2FnZW1lbnQgPSBhcy5kb3VibGUoZW5nYWdlbWVudCkNCiAgKSAlPiUNCiAgZ3JvdXBfYnkoc2VyaWVzX25hbWUsIG5ldHdvcmssIHNlYXNvbikgJT4lDQogIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgc2Vhc29uX3N0YXJ0ID0gbWluKGRhdGVfYWlyZWQpLA0KICAgIHNlYXNvbl9lbmQgPSBtYXgoZGF0ZV9haXJlZCksDQogICAgbl9lcGlzb2RlcyA9IG4oKSwNCiAgICBtZWFuX2ltZGIgPSBtZWFuKGltZGIsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9lbmdhZ2VtZW50ID0gbWVhbihlbmdhZ2VtZW50LCBuYS5ybSA9IFRSVUUpDQogICkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgZmlsdGVyKG5fZXBpc29kZXMgPiAxICYgIShzZWFzb24gJWluJSBjKCJNb3ZpZSIsICJTcGVjaWFsIikpKSAlPiUgIyBmaWx0ZXIgb3V0IG1vdmllIGV2ZW50cw0KICBhcnJhbmdlKHNlYXNvbl9zdGFydCwgc2Vhc29uX2VuZCkgJT4lDQogIG11dGF0ZSgNCiAgICBzZWFzb25faWQgPSBhcy5kb3VibGUocm93X251bWJlcigpKQ0KICApDQoNCnNlYXNvbl9pbmZvIDwtIHNlYXNvbl9pbmZvICU+JQ0KICBtdXRhdGUoDQogICAgcmFua2luZyA9IGNhc2Vfd2hlbigNCiAgICAgIHNlYXNvbl9pZCA9PSBoZWFkKGFycmFuZ2Uoc2Vhc29uX2luZm8sIGRlc2MobWVhbl9pbWRiKSksIDEpJHNlYXNvbl9pZCB+ICJCZXN0IFNlYXNvbiIsDQogICAgICBzZWFzb25faWQgJWluJSBoZWFkKGFycmFuZ2Uoc2Vhc29uX2luZm8sIGRlc2MobWVhbl9pbWRiKSksIDMpJHNlYXNvbl9pZCB+ICJUb3AgMyBTZWFzb24iLA0KICAgICAgc2Vhc29uX2lkID09IGhlYWQoYXJyYW5nZShzZWFzb25faW5mbywgbWVhbl9pbWRiKSwgMSkkc2Vhc29uX2lkIH4gIldvcnN0IFNlYXNvbiIsDQogICAgICBzZWFzb25faWQgJWluJSBoZWFkKGFycmFuZ2Uoc2Vhc29uX2luZm8sIG1lYW5faW1kYiksIDMpJHNlYXNvbl9pZCB+ICJCb3R0b20gMyBTZWFzb24iLA0KICAgICAgVFJVRSB+ICJPdGhlciINCiAgICApLA0KICAgIHJhbmtpbmcgPSBmYWN0b3IocmFua2luZywgbGV2ZWxzID0gYygiQmVzdCBTZWFzb24iLCAiVG9wIDMgU2Vhc29uIiwgIk90aGVyIiwgIkJvdHRvbSAzIFNlYXNvbiIsICJXb3JzdCBTZWFzb24iKSkNCiAgKQ0KYGBgDQoNClRoZSBhdmVyYWdlIFNjb29ieSBEb28gVFYgc2VyaWVzIGhhcyBgciBtZWFuKGNvdW50KHNlYXNvbl9pbmZvLCBzZXJpZXNfbmFtZSkkbilgIHNlYXNvbnMuIFRoYXQncyBzbyBmZXchDQoNCmBgYHtyIHNjb29ieSBzZWFzb24gaW1kYiB0aW1lbGluZX0NCiMgcGxvdCBzZWFzb24gb3ZlciB0aW1lDQpzZWFzb25faW5mbyAlPiUNCiAgcGxvdF9seSgNCiAgICB0eXBlID0gJ2JhcicsDQogICAgbW9kZSA9ICdtYXJrZXJzJywNCiAgICB4ID0gfnNlYXNvbl9pZCwNCiAgICB5ID0gfm1lYW5faW1kYiwNCiAgICBjb2xvciA9IH5yYW5raW5nLA0KICAgIGNvbG9ycyA9IGNvbG9yX3NjaGVtZTEsDQogICAgdGV4dCA9IH5wYXN0ZTAoIjxiPiIsIHNlcmllc19uYW1lLCAiIC0gU2Vhc29uICIsIHNlYXNvbiwgIjwvYj48YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiPGk+QWlyZWQgZnJvbSAiLCBzZWFzb25fc3RhcnQsICIgdG8gIiwgc2Vhc29uX2VuZCwgIiBvbiAiLCBuZXR3b3JrLCAiPC9pPjxicj48YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiTWVhbiBJTURiIFNjb3JlOiAiLCByb3VuZChtZWFuX2ltZGIsIDIpLCAiIChOdW1iZXIgb2YgUmV2aWV3czogIiwgcm91bmQobWVhbl9lbmdhZ2VtZW50KSwgIik8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiRXBpc29kZXM6ICIsIG5fZXBpc29kZXMsICI8YnI+IikNCiAgKSAlPiUNCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gJ0lNRGIgU2NvcmVzIGZvciBTY29vYnkgRG9vIFNlYXNvbnMgT3ZlciBUaW1lJywNCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnU2VxdWVudGlhbCBTZWFzb24gTnVtYmVyJywgc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSksDQogICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ01lYW4gSU1EYiBTY29yZScpLA0KICAgIGxlZ2VuZCA9IGxpc3Qob3JpZW50YXRpb24gPSAnaCcsIHkgPSAtMC4zKSwNCiAgICB3aWR0aCA9IDkwMCwNCiAgICBoZWlnaHQgPSA0MDANCiAgKQ0KYGBgDQoNCg0KIyMgQnkgRXBpc29kZXMNCg0KYGBge3Igc2Nvb2J5IGltZGIgdGltZWxpbmUsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEwfQ0Kc2Nvb2J5ZG9vICU+JQ0KICBsZWZ0X2pvaW4oc2VyaWVzX2luZm8sIGJ5ID0gYygic2VyaWVzX25hbWUiLCAibmV0d29yayIpKSAlPiUNCiAgICBmaWx0ZXIoDQogICAgIShpcy5uYShpbWRiKSksDQogICAgaW1kYiAhPSAiTlVMTCIsDQogICAgZW5nYWdlbWVudCAhPSAiTlVMTCIsDQogICAgIShzZWFzb24gJWluJSBjKCJNb3ZpZSIsICJTcGVjaWFsIikpDQogICkgJT4lDQogIHBsb3RfbHkoDQogICAgdHlwZSA9ICdzY2F0dGVyJywNCiAgICBtb2RlID0gJ21hcmtlcnMnLA0KICAgIHggPSB+aW5kZXgsDQogICAgeSA9IH5pbWRiLA0KICAgIGNvbG9yID0gfnJhbmtpbmcsDQogICAgY29sb3JzID0gY29sb3Jfc2NoZW1lMSwNCiAgICB0ZXh0ID0gfnBhc3RlMCgiPGI+IiwgdGl0bGUsICI8L2I+PGJyPiIsDQogICAgICAgICAgICAgICAgICAiPGk+U2Vhc29uICIsIHNlYXNvbiwgIiBvZiBTZXJpZXMgIiwgc2VyaWVzX25hbWUsICI8L2k+PGJyPjxicj4iLA0KICAgICAgICAgICAgICAgICAgIkFpcmVkICIsIGRhdGVfYWlyZWQsICIgb24gIiwgbmV0d29yaywgIjxicj4iLA0KICAgICAgICAgICAgICAgICAgIklNRGIgU2NvcmUgIiwgaW1kYiwgIiAoTnVtYmVyIG9mIFJldmlld3M6ICIsIGVuZ2FnZW1lbnQsICIpIikNCiAgKSAlPiUNCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gJ0lNRGIgc2NvcmVzIG9mIFNjb29ieSBEb28gZXBpc29kZXMgb3ZlciB0aW1lJywNCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnRXBpc29kZSBJbmRleCAoYWNjb3JkaW5nIHRvIFNjb29ieXBlZGlhKScpLA0KICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdJTURiIFNjb3JlJyksDQogICAgd2lkdGggPSA5MDAsDQogICAgaGVpZ2h0ID0gNDAwLA0KICAgIGxlZ2VuZCA9IGxpc3Qob3JpZW50YXRpb24gPSAnaCcsIHkgPSAtMC4zKQ0KICApDQpgYGANCg0KIyBNb25zdGVycyANCg0KYGBge3J9DQojIGNvbGxhcHNlIGNvbHVtbnMNCm1vbnN0ZXJzIDwtIHNjb29ieWRvbyAlPiUNCiAgbXV0YXRlKA0KICAgICMgY2F1Z2h0DQogICAgY2F1Z2h0X2ZyZWQgPSBzdHJfcmVwbGFjZShjYXVnaHRfZnJlZCwgIlRSVUUiLCAiRnJlZCIpLA0KICAgIGNhdWdodF9kYXBobmllID0gc3RyX3JlcGxhY2UoY2F1Z2h0X2RhcGhuaWUsICJUUlVFIiwgIkRhcGhuaWUiKSwNCiAgICBjYXVnaHRfdmVsbWEgPSBzdHJfcmVwbGFjZShjYXVnaHRfdmVsbWEsICJUUlVFIiwgIlZlbG1hIiksDQogICAgY2F1Z2h0X3NoYWdneSA9IHN0cl9yZXBsYWNlKGNhdWdodF9zaGFnZ3ksICJUUlVFIiwgIlNoYWdneSIpLA0KICAgIGNhdWdodF9zY29vYnkgPSBzdHJfcmVwbGFjZShjYXVnaHRfc2Nvb2J5LCAiVFJVRSIsICJTY29vYnkiKSwNCiAgICANCiAgICAjIGNhcHR1cmVkDQogICAgY2FwdHVyZWRfZnJlZCA9IHN0cl9yZXBsYWNlKGNhcHR1cmVkX2ZyZWQsICJUUlVFIiwgIkZyZWQiKSwNCiAgICBjYXB0dXJlZF9kYXBobmllID0gc3RyX3JlcGxhY2UoY2FwdHVyZWRfZGFwaG5pZSwgIlRSVUUiLCAiRGFwaG5pZSIpLA0KICAgIGNhcHR1cmVkX3ZlbG1hID0gc3RyX3JlcGxhY2UoY2FwdHVyZWRfdmVsbWEsICJUUlVFIiwgIlZlbG1hIiksDQogICAgY2FwdHVyZWRfc2hhZ2d5ID0gc3RyX3JlcGxhY2UoY2FwdHVyZWRfc2hhZ2d5LCAiVFJVRSIsICJTaGFnZ3kiKSwNCiAgICBjYXB0dXJlZF9zY29vYnkgPSBzdHJfcmVwbGFjZShjYXB0dXJlZF9zY29vYnksICJUUlVFIiwgIlNjb29ieSIpLA0KICAgIA0KICAgICMgdW5tYXNrZWQNCiAgICB1bm1hc2tfZnJlZCA9IHN0cl9yZXBsYWNlKHVubWFza19mcmVkLCAiVFJVRSIsICJGcmVkIiksDQogICAgdW5tYXNrX2RhcGhuaWUgPSBzdHJfcmVwbGFjZSh1bm1hc2tfZGFwaG5pZSwgIlRSVUUiLCAiRGFwaG5pZSIpLA0KICAgIHVubWFza192ZWxtYSA9IHN0cl9yZXBsYWNlKHVubWFza192ZWxtYSwgIlRSVUUiLCAiVmVsbWEiKSwNCiAgICB1bm1hc2tfc2hhZ2d5ID0gc3RyX3JlcGxhY2UodW5tYXNrX3NoYWdneSwgIlRSVUUiLCAiU2hhZ2d5IiksDQogICAgdW5tYXNrX3Njb29ieSA9IHN0cl9yZXBsYWNlKHVubWFza19zY29vYnksICJUUlVFIiwgIlNjb29ieSIpLA0KICApDQpgYGANCg0KIyMgV2hvIGNhdWdodCB0aGUgbW9uc3RlcnM/DQoNCmBgYHtyfQ0KIyB0aWR5IGRhdGEgZnJhbWUgZm9yIG1vbnN0ZXJzIGNhdWdodA0KY2F1Z2h0X2RmIDwtIG1vbnN0ZXJzDQpjYXVnaHRfZGYkY2F1Z2h0X2J5IDwtIGFwcGx5KGNhdWdodF9kZiAlPiUgc2VsZWN0KHN0YXJ0c193aXRoKCJjYXVnaHRfIikpLCAxLCBmdW5jdGlvbih4KSBwYXN0ZSh4W3ggIT0gIkZBTFNFIiAmIHggIT0gIk5VTEwiXSwgY29sbGFwc2UgPSAiLCIpKQ0KY2F1Z2h0X2RmJGNhdWdodF9ieVtpcy5uYShjYXVnaHRfZGYkY2F1Z2h0X2J5KV0gPC0gIk5vdCBDYXVnaHQiDQpjYXVnaHRfZGYgPC0gY2F1Z2h0X2RmICU+JQ0KICBzZXBhcmF0ZV9yb3dzKGNhdWdodF9ieSwgc2VwID0gIiwiKSAlPiUNCiAgZmlsdGVyKA0KICAgIGNhdWdodF9ieSAlaW4lIHRoZV9nYW5nDQogICkNCmBgYA0KDQpgYGB7cn0NCiMgdGFibGUgb2YgbW9zdCBjYXRjaGVzDQpjb3VudChjYXVnaHRfZGYsIGNhdWdodF9ieSkgJT4lDQogIGFycmFuZ2UoZGVzYyhuKSkNCmBgYA0KDQpgYGB7cn0NCiMgcGxvdCB0aGUgcmVsYXRpb25zaGlwIG92ZXIgdGltZQ0KY2F1Z2h0X2RmMiA8LSBjYXVnaHRfZGYgJT4lDQogIG11dGF0ZSgNCiAgICB5ZWFyID0geWVhcihkYXRlX2FpcmVkKSwNCiAgICByZWFsID0gaWZlbHNlKG1vbnN0ZXJfcmVhbCA9PSAiVFJVRSIsICJSZWFsIiwgIkZha2UiKSwNCiAgICBtb25zdGVyX2RldGFpbHMgPSBwYXN0ZTAobW9uc3Rlcl9uYW1lLCAiICgiLCByZWFsLCAiIE1vbnN0ZXIocykpIGluICciLCB0aXRsZSwgIicgb24gIiwgZGF0ZV9haXJlZCwgIi4iKQ0KICApICU+JQ0KICBncm91cF9ieSh5ZWFyLCBjYXVnaHRfYnkpICU+JQ0KICBkcGx5cjo6c3VtbWFyaXNlKA0KICAgIG4gPSBuKCksDQogICAgY2F1Z2h0X25hbWVzID0gcGFzdGUobW9uc3Rlcl9kZXRhaWxzLCBjb2xsYXBzZSA9ICJcbiIpDQogICkNCg0KcGxvdF9tb25zdGVyc19jYXVnaHQgPC0gZnVuY3Rpb24oZ2FuZ19tZW1iZXIsIGRmID0gY2F1Z2h0X2RmMikgew0KICBmaWcgPC0gZGYgJT4lDQogICAgZmlsdGVyKGNhdWdodF9ieSA9PSBnYW5nX21lbWJlcikgJT4lDQogICAgcGxvdF9seSgNCiAgICAgIHR5cGUgPSAnYmFyJywNCiAgICAgIHggPSB+eWVhciwNCiAgICAgIHkgPSB+biwNCiAgICAgIHRleHQgPSB+cGFzdGUwKCI8Yj5Nb25zdGVycyBDYXVnaHQ8L2I+PGJyPjxicj4iLA0KICAgICAgICAgICAgICAgICAgICAgY2F1Z2h0X25hbWVzKSwNCiAgICAgIHNob3dsZWdlbmQgPSBGQUxTRQ0KICAgICkgJT4lDQogICAgbGF5b3V0KA0KICAgICAgeGF4aXMgPSBsaXN0KA0KICAgICAgICBzaG93bGluZSA9IFRSVUUsDQogICAgICAgIHNob3d0aWNrbGFiZWxzID0gRkFMU0UsDQogICAgICAgIG1pcnJvciA9ICJ0aWNrcyIsDQogICAgICAgIGxpbmVjb2xvciA9IHRvUkdCKCJibGFjayIpLA0KICAgICAgICBsaW5ld2lkdGggPSAyLA0KICAgICAgICByYW5nZSA9IGMobWluKGNhdWdodF9kZjIkeWVhciksIG1heChjYXVnaHRfZGYyJHllYXIpKQ0KICAgICAgKSwNCiAgICAgIHlheGlzID0gbGlzdCgNCiAgICAgICAgdGl0bGUgPSB+cGFzdGUwKCI8Yj4iLCBnYW5nX21lbWJlciwgIjwvYj4iKSwNCiAgICAgICAgc2hvd2xpbmUgPSBUUlVFLA0KICAgICAgICBtaXJyb3IgPSAidGlja3MiLA0KICAgICAgICBsaW5lY29sb3IgPSB0b1JHQigiYmxhY2siKSwNCiAgICAgICAgbGluZXdpZHRoID0gMiwNCiAgICAgICAgcmFuZ2UgPSBjKG1pbihjYXVnaHRfZGYyJG4pLCBtYXgoY2F1Z2h0X2RmMiRuKSArIDEpDQogICAgICApDQogICAgKQ0KICANCiAgZmlnDQp9DQoNCnN1YnBsb3QoDQogIHBsb3RfbW9uc3RlcnNfY2F1Z2h0KCJGcmVkIiksDQogIHBsb3RfbW9uc3RlcnNfY2F1Z2h0KCJEYXBobmllIiksDQogIHBsb3RfbW9uc3RlcnNfY2F1Z2h0KCJWZWxtYSIpLA0KICBwbG90X21vbnN0ZXJzX2NhdWdodCgiU2hhZ2d5IiksDQogIHBsb3RfbW9uc3RlcnNfY2F1Z2h0KCJTY29vYnkiKSwNCiAgbnJvd3MgPSBsZW5ndGgodGhlX2dhbmcpLA0KICBzaGFyZVggPSBGQUxTRSwNCiAgc2hhcmVZID0gRkFMU0UsDQogIHRpdGxlWCA9IEZBTFNFLA0KICB0aXRsZVkgPSBUUlVFDQopICU+JQ0KICBsYXlvdXQoDQogICAgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywNCiAgICB0aXRsZSA9ICc8Yj48aT5Nb25zdGVycyBDYXB0dXJlZCBBbm51YWx5IEJ5Li4uPC9pPjwvYj4nLA0KICAgIHdpZHRoID0gOTAwLA0KICAgIGhlaWdodCA9IDYwMCwNCiAgICBsZWdlbmQgPSBsaXN0KG9yaWVudGF0aW9uID0gJ2gnKQ0KICApDQpgYGANCg0KDQojIyBXaG8gd2FzIGNhcHR1cmVkIGJ5IG1vbnN0ZXJzIHRoZSBtb3N0Pw0KDQpgYGB7cn0NCiMgdGlkeSBkYXRhIGZyYW1lIGZvciBjYXB0dXJlZCBieSB0aGUgbW9uc3Rlcg0KY2FwdHVyZWRfZGYgPC0gbW9uc3RlcnMNCmNhcHR1cmVkX2RmJGNhcHR1cmVkIDwtIGFwcGx5KGNhcHR1cmVkX2RmICU+JSBzZWxlY3Qoc3RhcnRzX3dpdGgoImNhcHR1cmVkXyIpKSwgMSwgZnVuY3Rpb24oeCkgcGFzdGUoeFt4ICE9ICJGQUxTRSIgJiB4ICE9ICJOVUxMIl0sIGNvbGxhcHNlID0gIiwiKSkNCiNjYXB0dXJlZCRjYXB0dXJlZF9ieVtpcy5uYShjYXB0dXJlZCRjYXVnaHRfYnkpXSA8LSAiTm90IENhdWdodCINCmNhcHR1cmVkX2RmIDwtIGNhcHR1cmVkX2RmICU+JQ0KICBzZXBhcmF0ZV9yb3dzKGNhcHR1cmVkLCBzZXAgPSAiLCIpICU+JQ0KICBmaWx0ZXIoDQogICAgY2FwdHVyZWQgJWluJSB0aGVfZ2FuZw0KICApDQpgYGANCg0KYGBge3J9DQpjb3VudChjYXB0dXJlZF9kZiwgY2FwdHVyZWQpICU+JQ0KICBhcnJhbmdlKGRlc2MobikpDQpgYGANCg0KYGBge3J9DQojIHBsb3QgdGhlIHJlbGF0aW9uc2hpcA0KY2FwdHVyZWRfZGYyIDwtIGNhcHR1cmVkX2RmICU+JQ0KICBtdXRhdGUoDQogICAgeWVhciA9IHllYXIoZGF0ZV9haXJlZCksDQogICAgcmVhbCA9IGlmZWxzZShtb25zdGVyX3JlYWwgPT0gIlRSVUUiLCAiUmVhbCIsICJGYWtlIiksDQogICAgbW9uc3Rlcl9kZXRhaWxzID0gcGFzdGUwKG1vbnN0ZXJfbmFtZSwgIiAoIiwgcmVhbCwgIiBNb25zdGVyKHMpKSBpbiAnIiwgdGl0bGUsICInIG9uICIsIGRhdGVfYWlyZWQsICIuIikNCiAgKSAlPiUNCiAgZ3JvdXBfYnkoeWVhciwgY2FwdHVyZWQpICU+JQ0KICBkcGx5cjo6c3VtbWFyaXNlKA0KICAgIG4gPSBuKCksDQogICAgY2FwdHVyZWRfZGV0YWlscyA9IHBhc3RlKG1vbnN0ZXJfZGV0YWlscywgY29sbGFwc2UgPSAiXG4iKQ0KICApDQoNCnBsb3RfbW9uc3RlcnNfY2FwdHVyZXMgPC0gZnVuY3Rpb24oZ2FuZ19tZW1iZXIsIGRmID0gY2FwdHVyZWRfZGYyKSB7DQogIGZpZyA8LSBkZiAlPiUNCiAgICBmaWx0ZXIoY2FwdHVyZWQgPT0gZ2FuZ19tZW1iZXIpICU+JQ0KICAgIHBsb3RfbHkoDQogICAgICB0eXBlID0gJ2JhcicsDQogICAgICB4ID0gfnllYXIsDQogICAgICB5ID0gfm4sDQogICAgICB0ZXh0ID0gfnBhc3RlMCgiPGI+Q2FwdHVyZWQgQnkuLi48L2I+PGJyPjxicj4iLA0KICAgICAgICAgICAgICAgICAgICAgY2FwdHVyZWRfZGV0YWlscyksDQogICAgICBzaG93bGVnZW5kID0gRkFMU0UNCiAgICApICU+JQ0KICAgIGxheW91dCgNCiAgICAgIHhheGlzID0gbGlzdCgNCiAgICAgICAgc2hvd2xpbmUgPSBUUlVFLA0KICAgICAgICBzaG93dGlja2xhYmVscyA9IEZBTFNFLA0KICAgICAgICBtaXJyb3IgPSAidGlja3MiLA0KICAgICAgICBsaW5lY29sb3IgPSB0b1JHQigiYmxhY2siKSwNCiAgICAgICAgbGluZXdpZHRoID0gMiwNCiAgICAgICAgcmFuZ2UgPSBjKG1pbihjYXB0dXJlZF9kZjIkeWVhciksIG1heChjYXB0dXJlZF9kZjIkeWVhcikpDQogICAgICApLA0KICAgICAgeWF4aXMgPSBsaXN0KA0KICAgICAgICB0aXRsZSA9IH5wYXN0ZTAoIjxiPiIsIGdhbmdfbWVtYmVyLCAiPC9iPiIpLA0KICAgICAgICBzaG93bGluZSA9IFRSVUUsDQogICAgICAgIG1pcnJvciA9ICJ0aWNrcyIsDQogICAgICAgIGxpbmVjb2xvciA9IHRvUkdCKCJibGFjayIpLA0KICAgICAgICBsaW5ld2lkdGggPSAyLA0KICAgICAgICByYW5nZSA9IGMobWluKGNhcHR1cmVkX2RmMiRuKSwgbWF4KGNhcHR1cmVkX2RmMiRuKSArIDEpDQogICAgICApDQogICAgKQ0KICANCiAgZmlnDQp9DQoNCnN1YnBsb3QoDQogIHBsb3RfbW9uc3RlcnNfY2FwdHVyZXMoIkZyZWQiKSwNCiAgcGxvdF9tb25zdGVyc19jYXB0dXJlcygiRGFwaG5pZSIpLA0KICBwbG90X21vbnN0ZXJzX2NhcHR1cmVzKCJWZWxtYSIpLA0KICBwbG90X21vbnN0ZXJzX2NhcHR1cmVzKCJTaGFnZ3kiKSwNCiAgcGxvdF9tb25zdGVyc19jYXB0dXJlcygiU2Nvb2J5IiksDQogIG5yb3dzID0gbGVuZ3RoKHRoZV9nYW5nKSwNCiAgc2hhcmVYID0gRkFMU0UsDQogIHNoYXJlWSA9IEZBTFNFLA0KICB0aXRsZVggPSBGQUxTRSwNCiAgdGl0bGVZID0gVFJVRQ0KKSAlPiUNCiAgbGF5b3V0KA0KICAgIG1vZGUgPSAnbGluZXMrbWFya2VycycsDQogICAgdGl0bGUgPSAnPGI+PGk+Smlua2llcyEgLi4uIGhhcyBiZWVuIGNhcHR1cmVkIGJ5IHRoZSBtb25zdGVyITwvaT48L2I+JywNCiAgICB3aWR0aCA9IDkwMCwNCiAgICBoZWlnaHQgPSA2MDAsDQogICAgbGVnZW5kID0gbGlzdChvcmllbnRhdGlvbiA9ICdoJykNCiAgKQ0KYGBgDQoNCiMjIFdobyB1bm1hc2tlZCB0aGUgbW9zdCBtb25zdGVycz8NCg0KYGBge3J9DQojIHRpZHkgZGF0YSBmcmFtZSBmb3IgY2FwdHVyZWQgYnkgdGhlIG1vbnN0ZXINCnVubWFza2VkX2RmIDwtIG1vbnN0ZXJzDQp1bm1hc2tlZF9kZiR1bm1hc2tlZF9ieSA8LSBhcHBseSh1bm1hc2tlZF9kZiAlPiUgc2VsZWN0KHN0YXJ0c193aXRoKCJ1bm1hc2tfIikpLCAxLCBmdW5jdGlvbih4KSBwYXN0ZSh4W3ggIT0gIkZBTFNFIiAmIHggIT0gIk5VTEwiXSwgY29sbGFwc2UgPSAiLCIpKQ0KdW5tYXNrZWRfZGYkdW5tYXNrZWRfYnlbaXMubmEodW5tYXNrZWRfZGYkdW5tYXNrZWRfYnkpXSA8LSAiTm90IFVubWFza2VkIg0KdW5tYXNrZWRfZGYgPC0gdW5tYXNrZWRfZGYgJT4lDQogIHNlcGFyYXRlX3Jvd3ModW5tYXNrZWRfYnksIHNlcCA9ICIsIikgJT4lDQogIGZpbHRlcigNCiAgICB1bm1hc2tlZF9ieSAlaW4lIHRoZV9nYW5nDQogICkNCmBgYA0KDQpgYGB7cn0NCmNvdW50KHVubWFza2VkX2RmLCB1bm1hc2tlZF9ieSkgJT4lDQogIGFycmFuZ2UoZGVzYyhuKSkNCmBgYA0KDQpgYGB7cn0NCiMgcGxvdCB0aGUgcmVsYXRpb25zaGlwDQp1bm1hc2tlZF9kZjIgPC0gdW5tYXNrZWRfZGYgJT4lDQogIG11dGF0ZSgNCiAgICB5ZWFyID0geWVhcihkYXRlX2FpcmVkKSwNCiAgICByZWFsID0gaWZlbHNlKG1vbnN0ZXJfcmVhbCA9PSAiVFJVRSIsICJSZWFsIiwgIkZha2UiKSwNCiAgICBtb25zdGVyX2RldGFpbHMgPSBwYXN0ZTAobW9uc3Rlcl9uYW1lLCAiICgiLCByZWFsLCAiIE1vbnN0ZXIocykpIGluICciLCB0aXRsZSwgIicgb24gIiwgZGF0ZV9haXJlZCwgIi4iKQ0KICApICU+JQ0KICBncm91cF9ieSh5ZWFyLCB1bm1hc2tlZF9ieSkgJT4lDQogIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgbiA9IG4oKSwNCiAgICB1bm1hc2tfZGV0YWlscyA9IHBhc3RlKG1vbnN0ZXJfZGV0YWlscywgY29sbGFwc2UgPSAiXG4iKQ0KICApDQoNCnBsb3RfbW9uc3RlcnNfdW5tYXNrcyA8LSBmdW5jdGlvbihnYW5nX21lbWJlciwgZGYgPSB1bm1hc2tlZF9kZjIpIHsNCiAgZmlnIDwtIGRmICU+JQ0KICAgIGZpbHRlcih1bm1hc2tlZF9ieSA9PSBnYW5nX21lbWJlcikgJT4lDQogICAgcGxvdF9seSgNCiAgICAgIHR5cGUgPSAnYmFyJywNCiAgICAgIHggPSB+eWVhciwNCiAgICAgIHkgPSB+biwNCiAgICAgIHRleHQgPSB+cGFzdGUwKCI8Yj5Vbm1hc2tlZCBCeS4uLjwvYj48YnI+PGJyPiIsDQogICAgICAgICAgICAgICAgICAgICB1bm1hc2tfZGV0YWlscyksDQogICAgICBzaG93bGVnZW5kID0gRkFMU0UNCiAgICApICU+JQ0KICAgIGxheW91dCgNCiAgICAgIHhheGlzID0gbGlzdCgNCiAgICAgICAgc2hvd2xpbmUgPSBUUlVFLA0KICAgICAgICBzaG93dGlja2xhYmVscyA9IEZBTFNFLA0KICAgICAgICBtaXJyb3IgPSAidGlja3MiLA0KICAgICAgICBsaW5lY29sb3IgPSB0b1JHQigiYmxhY2siKSwNCiAgICAgICAgbGluZXdpZHRoID0gMiwNCiAgICAgICAgcmFuZ2UgPSBjKG1pbih1bm1hc2tlZF9kZjIkeWVhciksIG1heCh1bm1hc2tlZF9kZjIkeWVhcikpDQogICAgICApLA0KICAgICAgeWF4aXMgPSBsaXN0KA0KICAgICAgICB0aXRsZSA9IH5wYXN0ZTAoIjxiPiIsIGdhbmdfbWVtYmVyLCAiPC9iPiIpLA0KICAgICAgICBzaG93bGluZSA9IFRSVUUsDQogICAgICAgIG1pcnJvciA9ICJ0aWNrcyIsDQogICAgICAgIGxpbmVjb2xvciA9IHRvUkdCKCJibGFjayIpLA0KICAgICAgICBsaW5ld2lkdGggPSAyLA0KICAgICAgICByYW5nZSA9IGMobWluKHVubWFza2VkX2RmMiRuKSwgbWF4KHVubWFza2VkX2RmMiRuKSArIDEpDQogICAgICApDQogICAgKQ0KICANCiAgZmlnDQp9DQoNCnN1YnBsb3QoDQogIHBsb3RfbW9uc3RlcnNfdW5tYXNrcygiRnJlZCIpLA0KICBwbG90X21vbnN0ZXJzX3VubWFza3MoIkRhcGhuaWUiKSwNCiAgcGxvdF9tb25zdGVyc191bm1hc2tzKCJWZWxtYSIpLA0KICBwbG90X21vbnN0ZXJzX3VubWFza3MoIlNoYWdneSIpLA0KICBwbG90X21vbnN0ZXJzX3VubWFza3MoIlNjb29ieSIpLA0KICBucm93cyA9IGxlbmd0aCh0aGVfZ2FuZyksDQogIHNoYXJlWCA9IEZBTFNFLA0KICBzaGFyZVkgPSBGQUxTRSwNCiAgdGl0bGVYID0gRkFMU0UsDQogIHRpdGxlWSA9IFRSVUUNCikgJT4lDQogIGxheW91dCgNCiAgICBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLA0KICAgIHRpdGxlID0gJzxiPjxpPk1vbnN0ZXJzIFVubWFza2VkIEFubnVhbHkgQnkuLi48L2k+PC9iPicsDQogICAgd2lkdGggPSA5MDAsDQogICAgaGVpZ2h0ID0gNjAwLA0KICAgIGxlZ2VuZCA9IGxpc3Qob3JpZW50YXRpb24gPSAnaCcpDQogICkNCmBgYA0KDQojIyBXaGF0IG1vbnN0ZXIgdHlwZXMgYXJlIG1vc3QgbGlrZWx5IHRvIGdldCBhd2F5Pw0KDQpgYGB7cn0NCm5fbm90X2NhdWdodCA8LSBucm93KGZpbHRlcihzY29vYnlkb28sIGNhdWdodF9ub3QgPT0gIlRSVUUiKSkNCmdvdF9hd2F5X3JhdGUgPC0gcGFzdGUwKHJvdW5kKDEwMCAqIChuX25vdF9jYXVnaHQgLyBucm93KHNjb29ieWRvbykpLCAyKSwgIiUiKQ0KYGBgDQoNCkZpcnN0IG9mIGFsbCwgdGhlIG1vbnN0ZXIocykgZ290IGF3YXkgYXQgdGhlIGVuZCBvZiBvbmx5IGByIG5fbm90X2NhdWdodGAgb3V0IG9mIGByIG5yb3coc2Nvb2J5ZG9vKWAgZXBpc29kZXMgKHJhdGUgb2YgYHIgZ290X2F3YXlfcmF0ZWApLiBPZiB0aG9zZSBgciBuX25vdF9jYXVnaHRgLCB0aGUgZm9sbG93aW5nIGJyZWFrcyBkb3duIHRoZSBzdWNjZXNzIHJhdGUgYnkgbW9uc3RlciB0eXBlLg0KDQpgYGB7cn0NCmVzY2FwZV9zdGF0cyA8LSBzY29vYnlkb28gJT4lDQogIGZpbHRlcihjYXVnaHRfbm90ID09ICJUUlVFIikgJT4lDQogICMgbG9va3MgbGlrZSB0aGVyZSB3ZXJlIGEgZmV3IG1pc3BlbGxpbmdzDQogIG11dGF0ZShtb25zdGVyX3R5cGUgPSBpZmVsc2Uoc3RyX2RldGVjdChtb25zdGVyX3R5cGUsICIoRGlzZ3Vpc2VkfERpc2d1aXNlZHxEaXN1Z2lzZWQpIiksICJEaXNndWlzZWQiLCBtb25zdGVyX3R5cGUpKSAlPiUNCiAgc2VwYXJhdGVfcm93cyhtb25zdGVyX3R5cGUsIHNlcCA9ICIsIikgJT4lDQogIGdyb3VwX2J5KG1vbnN0ZXJfdHlwZSkgJT4lDQogIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgbl9lc2NhcGVkID0gbigpLA0KICAgIG1lYW5faW1kYl9lc2NhcGVkID0gbWVhbihhcy5kb3VibGUoaW1kYikpDQogICkNCg0KaW1kYl9lZmZlY3RfY2F0ZWdvcmllcyA8LSBjKA0KICAiQmV0dGVyIFRoYW4gQXZlcmFnZSBFcGlzb2RlIiwNCiAgIlNsaWdodGx5IEJldHRlciBUaGFuIEF2ZXJhZ2UgRXBpc29kZSIsDQogICJTbGlnaHRseSBXb3JzZSBUaGFuIEF2ZXJhZ2UgRXBpc29kZSIsDQogICJXb3JzZSBUaGFuIEF2ZXJhZ2UgRXBpc29kZSINCikNCg0KZXNjYXBlX3N0YXRzMiA8LSBzY29vYnlkb28gJT4lDQogIG11dGF0ZShtb25zdGVyX3R5cGUgPSBpZmVsc2Uoc3RyX2RldGVjdChtb25zdGVyX3R5cGUsICIoRGlzZ3Vpc2VkfERpc2d1aXNlZHxEaXN1Z2lzZWQpIiksICJEaXNndWlzZWQiLCBtb25zdGVyX3R5cGUpKSAlPiUNCiAgc2VwYXJhdGVfcm93cyhtb25zdGVyX3R5cGUsIHNlcCA9ICIsIikgJT4lDQogIGZpbHRlcihpbWRiICE9ICJOVUxMIikgJT4lDQogIGdyb3VwX2J5KG1vbnN0ZXJfdHlwZSkgJT4lDQogIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgbl90b3RhbCA9IG4oKSwNCiAgICBtZWFuX2ltZGIgPSBtZWFuKGFzLmRvdWJsZShpbWRiKSkNCiAgKSAlPiUNCiAgaW5uZXJfam9pbihlc2NhcGVfc3RhdHMpICU+JQ0KICBtdXRhdGUoDQogICAgZXNjYXBlX3JhdGUgPSByb3VuZCgxMDAgKiAobl9lc2NhcGVkIC8gbl90b3RhbCkpLA0KICAgIGVzY2FwZV9pbWRiX2VmZmVjdCA9IG1lYW5faW1kYl9lc2NhcGVkIC0gbWVhbl9pbWRiLA0KICAgIGltZGJfZWZmZWN0X2NhdGVnb3J5ID0gY2FzZV93aGVuKA0KICAgICAgZXNjYXBlX2ltZGJfZWZmZWN0IDwgLTEgfiAiV29yc2UgVGhhbiBBdmVyYWdlIEVwaXNvZGUiLA0KICAgICAgZXNjYXBlX2ltZGJfZWZmZWN0IDwgMCB+ICJTbGlnaHRseSBXb3JzZSBUaGFuIEF2ZXJhZ2UgRXBpc29kZSIsDQogICAgICBlc2NhcGVfaW1kYl9lZmZlY3QgPCAxIH4gIlNsaWdodGx5IEJldHRlciBUaGFuIEF2ZXJhZ2UgRXBpc29kZSIsDQogICAgICBlc2NhcGVfaW1kYl9lZmZlY3QgPj0gMSB+ICJCZXR0ZXIgVGhhbiBBdmVyYWdlIEVwaXNvZGUiDQogICAgKSwNCiAgICBpbWRiX2VmZmVjdF9jYXRlZ29yeSA9IGZhY3RvcihpbWRiX2VmZmVjdF9jYXRlZ29yeSwgbGV2ZWxzID0gaW1kYl9lZmZlY3RfY2F0ZWdvcmllcykNCiAgKQ0KDQplc2NhcGVfc3RhdHMyICU+JQ0KICBwbG90X2x5KA0KICAgIHR5cGUgPSAnYmFyJywNCiAgICB4ID0gfmZjdF9yZW9yZGVyKG1vbnN0ZXJfdHlwZSwgZXNjYXBlX3JhdGUsIC5kZXNjID0gVFJVRSksDQogICAgeSA9IH5lc2NhcGVfcmF0ZSwNCiAgICBjb2xvciA9IH5pbWRiX2VmZmVjdF9jYXRlZ29yeSwNCiAgICBjb2xvcnMgPSBjb2xvcl9zY2hlbWUyLA0KICAgIHRleHQgPSB+cGFzdGUwKCI8Yj4iLCBtb25zdGVyX3R5cGUsICI8L2I+PGJyPjxicj4iLCANCiAgICAgICAgICAgICAgICAgICAiRXNjYXBlZCAiLCBuX2VzY2FwZWQsICIgb3V0IG9mICIsIG5fdG90YWwsICIgZXBpc29kZXMuPGJyPiIsDQogICAgICAgICAgICAgICAgICAgIkF2ZXJhZ2UgRXBpc29kZSBJTURiIFNjb3JlIChBbGwpOiAiLCByb3VuZChtZWFuX2ltZGIsIDEpLCAiPGJyPiIsDQogICAgICAgICAgICAgICAgICAgIkF2ZXJhZ2UgRXBpc29kZSBJTURiIFNjb3JlIChFc2NhcGVkKTogIiwgcm91bmQobWVhbl9pbWRiX2VzY2FwZWQsIDEpKQ0KICApICU+JQ0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAnSG93IG9mdGVuIGRvIFNjb29ieSBEb28gbW9uc3RlcnMgZ28gdW5jYXVnaHQsIGFuZCBob3cgZG9lcyB0aGF0IGFmZmVjdCB0aGUgZXBpc29kZT8nLA0KICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJNb25zdGVyIFR5cGUiKSwNCiAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiRXNjYXBlIFJhdGUgKCUpIiksDQogICAgbGVnZW5kID0gbGlzdChvcmllbnRhdGlvbiA9ICdoJywgeSA9IC0wLjMpLA0KICAgIHdpZHRoID0gOTAwLA0KICAgIGhlaWdodCA9IDUwMA0KICApDQpgYGANCg==